//
// ResourceManager.cpp
//

#include "ResourceManager.h"
#include "CompressedResource.h"

const CResourceData CResourceData::null_object;

CResourceCacheTask::CResourceCacheTask(void)
{
	_pRes = NULL;
}

CResourceCacheTask::CResourceCacheTask(CCompressedResource *pResource, const std::wstring &szLocation)
	: _szLocation(szLocation), _pRes(pResource)
{
}

CResourceCacheTask::CResourceCacheTask(const CResourceCacheTask &task)
	: CLoaderTask(task), _pRes(task._pRes), _pData(task._pData)
{
}

CResourceCacheTask &CResourceCacheTask::operator=(const CResourceCacheTask &task)
{
	if(this == &task) return *this;
	CLoaderTask::operator=(task);
	_pRes = task._pRes;
	_pData = task._pData;
	return *this;
}

CResourceCacheTask::~CResourceCacheTask(void)
{
}

void CResourceCacheTask::begin(void)
{
	if(!_pRes) return ;
	_pRes->lock();
	if(!_pRes->locateFile(_szLocation.c_str())) {
		_pRes->unlock();
		return ;
	}
	size_t size = _pRes->getCurrentFileSize();
	if(size == 0) {
		_pRes->unlock();
		return ;
	}
	_pData.reset(size);
	if(!_pRes->fetchCurrentFile((BYTE *)_pData.lock(), size)) {
		_pData.unlock();
		_pData.release();
	} else {
		_pData.unlock();
	}
	_pRes->unlock();
}

CResourceManager::CResourceManager(void)
{
	LOG_WRITE(DS_RESOURCE_MGR_LOADED);
	_queue = dcnew CLoaderTaskQueue();
}

CResourceManager::~CResourceManager(void)
{
	LOG_WRITE(DS_RESOURCE_MGR_UNLOADED);
	delete _queue;
	cleanCache();
	cleanPackages();
}

bool CResourceManager::addPackage(const std::wstring &szPackageFileName)
{
	CCompressedResource *res;
	
	res = new(std::nothrow) CCompressedResource();
	if(res == NULL) return false;
	if(!res->open(szPackageFileName.c_str())) {
		delete res;
		return false;
	}
	int slash = szPackageFileName.find_last_of(L'/');
	int dot = szPackageFileName.find_last_of(L'.');
	_resources.insert(make_pair(szPackageFileName.substr(slash + 1, dot - slash - 1), res));
	LOG_WRITE(DS_RESOURCE_PACK_ADDED, szPackageFileName.c_str());
	return true;
}

void CResourceManager::removePackage(const std::wstring &szPackageFileName)
{
	auto i = _resources.find(szPackageFileName);
	if(i != _resources.end()) {
		_resources.erase(i);
		LOG_WRITE(DS_RESOURCE_PACK_REMOVED, szPackageFileName.c_str());
	}
}

void CResourceManager::cleanPackages(void)
{
	std::for_each(_resources.begin(), _resources.end(),
		[](std::pair<std::wstring, CCompressedResource *> p) {
			dcdelete p.second;
		});
	_resources.clear();
	LOG_WRITE(DS_ALL_RESPACKS_REMOVED);
}

CCompressedResource *CResourceManager::getPackage(const std::wstring &szPackageFileName)
{
	auto iter = _resources.find(szPackageFileName);
	if(iter != _resources.end()) {
		return iter->second;
	}
	LOG_WRITE(DS_RESOURCE_PACK_NOT_FOUND, szPackageFileName.c_str());
	return NULL;
}

const CResourceData CResourceManager::fetchObject(const std::wstring &szObjectUrl)
{
	LOG_WRITE(DS_FETCHING_RESOURCE_FILE, szObjectUrl.c_str());

	auto iter = _cache.find(szObjectUrl);
	if(iter != _cache.end()) {
		iter->second->wait();
		return iter->second->getData();
	}

	size_t slash = szObjectUrl.find_first_of(L'/');
	CCompressedResource *res = getPackage(szObjectUrl.substr(0, slash));
	if(res) {
		res->lock();
		std::wstring location = szObjectUrl.substr(slash + 1);
		if(!res->locateFile(location.c_str())) {
			LOG_WRITE(DS_RESOURCE_NOT_FOUND);
			res->unlock();
			return CResourceData::null_object;
		}
		size_t size = res->getCurrentFileSize();
		if(size != 0) {
			CResourceData data(size);
			res->fetchCurrentFile((BYTE *)data.lock(), data.size());
			data.unlock();
			res->unlock();
			return data;
		} else {
			LOG_WRITE(DS_RESOURCE_NOT_FOUND, szObjectUrl.c_str());
		}
		res->unlock();
	} else {
		LOG_WRITE(DS_GET_PACKAGE_FAILED);
	}
	return CResourceData::null_object;
}

const CResourceCacheTask *CResourceManager::getCacheTask(const std::wstring &szObjectUrl)
{
	auto iter = _cache.find(szObjectUrl);
	if(iter != _cache.end()) {
		return iter->second;
	}
	LOG_WRITE(DS_CACHE_TASK_NOT_FOUND, szObjectUrl.c_str());
	return NULL;
}

bool CResourceManager::aliasObject(const std::wstring &szObjectUrl, const std::wstring &szNewName)
{
	if(!cacheObject(szObjectUrl)) {
		LOG_WRITE(DS_CACHE_OBJECT_FAILED);
		return false;
	}
	const CResourceCacheTask *pTask = getCacheTask(szObjectUrl);
	if(pTask == NULL) return false;
	_cache.insert(std::make_pair(szNewName, dcnew CResourceCacheTask(*pTask)));
	return true;
}

bool CResourceManager::cacheObject(const std::wstring &szObjectUrl)
{
	CResourceCacheTask *task;
	
	size_t slash = szObjectUrl.find_first_of(L'/');
	CCompressedResource *res = getPackage(szObjectUrl.substr(0, slash));

	if(res) {
		task = new(std::nothrow) CResourceCacheTask(res, szObjectUrl.substr(slash + 1));
		if(task == NULL) return false;
		if(!_queue->addTask(task)) {
			LOG_WRITE(DS_ADD_TASK_FAILED);
			delete task;
			return false;
		}
		_cache.insert(make_pair(szObjectUrl, task));
		return true;
	} else {
		LOG_WRITE(DS_GET_PACKAGE_FAILED, szObjectUrl.substr(0, slash));
	}
	return false;
}

bool CResourceManager::isCachedObjectReady(const std::wstring &szObjectUrl)
{
	auto iter = _cache.find(szObjectUrl);
	if(iter != _cache.end() && iter->second->isCompleted()) {
		return true;
	}
	return false;
}

bool CResourceManager::isEverythingCached(void)
{
	return _queue->isCompleted();
}

void CResourceManager::cleanCache(void)
{
	std::for_each(_cache.begin(), _cache.end(),
		[](std::pair<std::wstring, CResourceCacheTask *> p) {
			dcdelete p.second;
		});
	_cache.clear();
	LOG_WRITE(DS_ALL_RESCACHE_REMOVED);
}

void CResourceManager::reset(void)
{
	dcdelete _queue;
	_queue = dcnew CLoaderTaskQueue();
	cleanCache();
	cleanPackages();
}

void CResourceManager::pause(void)
{
	_queue->pause();
}

void CResourceManager::resume(void)
{
	_queue->resume();
}
